home *** CD-ROM | disk | FTP | other *** search
/ 130 MIDI Tool Box / 130 MIDI Tool Box.iso / ptmid / ptmid.c < prev    next >
C/C++ Source or Header  |  1994-01-08  |  12KB  |  410 lines

  1. /*
  2.  * ptmid.c: Creates Protracker MODule files from MIDI files.
  3.  * (My first attempt at Hungarian Notation.. wince!)
  4.  *
  5.  * Author: Andrew Scott  (c)opyright 1994
  6.  *
  7.  * Date: 17/11/1993 ver 0.0
  8.  *       8/1/1994   ver 0.1
  9.  */
  10.  
  11. #include <stdio.h>
  12. #include <stdlib.h>
  13. #include <string.h>
  14. #include <ctype.h>
  15. #include <io.h>
  16. #include "ptmid.h"
  17.  
  18. char bDrumch = 9, szId[5] = "M.K.";
  19. int fNocopy = 0, fQuiet = 0, fExtend = 0;
  20. int wPatmax = 64, wPtchan = 4, wQuantval = 16;
  21. SI *rgpsiDrum[128], **rgppsiIns[129], *psiTree = NULL;
  22. Sz szTitle = "Converted by PTMID!", szQuant = NULL;
  23.  
  24. /*
  25.  * Init: Does all those initialization things (which aren't too involved).
  26.  */
  27. static void Init()
  28. {
  29.     int i;
  30.  
  31.     rgppsiIns[128] = NULL; /** Make sure sample-info arrays are clear **/
  32.     for (i = 128; i--; ) {
  33.         rgpsiDrum[i] = NULL;
  34.         rgppsiIns[i] = NULL;
  35.     }
  36. }
  37.  
  38. /*
  39.  * BuildFn: Builds a full filename given a string which is the old filename,
  40.  * and a default extension to use if one is not present in the string. After
  41.  * building the new filename, any extension in the old name is removed.
  42.  */
  43. void BuildFn(Fn fnNew, Sz fnOld, Sz fnExt)
  44. {
  45.     Sz fnT = fnNew;
  46.     int fExt = 0;
  47.  
  48.     while (*fnOld) {
  49.         if ('.' == (*(fnT++) = *fnOld)) { /** Copy a char, test for extension **/
  50.             fExt = 1;
  51.             *fnOld = 0; /** yes.. note extension exists and remove it **/
  52.         }
  53.         fnOld++;
  54.     }
  55.     if (!fExt) { /** If no extension found **/
  56.         *(fnT++) = '.';
  57.         while ((*(fnT++) = *(fnExt++))); /** copy the default one - fnExt **/
  58.     } else
  59.         *fnT = 0;
  60. }
  61.  
  62. /*
  63.  * SzReadPfile: Reads the next string from the textfile given and returns it.
  64.  * If file is at end, returns NULL.
  65.  */
  66. Sz SzReadPfile(FILE *pfileText)
  67. {
  68.     int ch, cch = 0, cchMac = 80;
  69.     Sz szT, szStart;
  70.  
  71.     if (feof(pfileText))
  72.         return NULL;
  73.     szStart = szT = (Sz) malloc(80); /** Set aside 80 characters for line **/
  74.     while ((ch = getc(pfileText)) != EOF && ch != '\n') {
  75.         *szT = ch;
  76.         if (++cch == cchMac) { /** If that's not enough **/
  77.             cchMac += 40;
  78.             szStart = (Sz) realloc(szStart, cchMac); /** increase in steps of 40 **/
  79.             szT = szStart + cch;
  80.         } else
  81.             szT++;
  82.     }
  83.     *szT = 0;
  84.     return (Sz) realloc(szStart, cch + 1);
  85. }
  86.  
  87. /*
  88.  * PsiAddsample: Given a sample's filename, will look it up in the tree
  89.  * and return a pointer to it if it exists, else will create it and return
  90.  * a pointer to the newly created entry.
  91.  */
  92. SI *PsiAddsample(Sz fnSample)
  93. {
  94.     SI *psiT;
  95.  
  96.     if (NULL == psiTree) { /** If nothing in tree **/
  97.         psiT = psiTree = (SI *) malloc(sizeof(SI)); /** create root node **/
  98.         psiT->fnSample = strdup(fnSample);
  99.         psiT->pitch = -1;
  100.         psiT->sample = -1;
  101.         psiT->psiL = psiT->psiR = NULL;
  102.     } else { /** Else **/
  103.         SI *psiOld;
  104.         int cmp;
  105.  
  106.         psiT = psiTree;
  107.         while (psiT != NULL) { /** find spot for sample in tree **/
  108.             psiOld = psiT;
  109.             cmp = strcmp(psiT->fnSample, fnSample);
  110.             if (!cmp)
  111.                 break;
  112.             else if (0 > cmp)
  113.                 psiT = psiT->psiL;
  114.             else
  115.                 psiT = psiT->psiR;
  116.         }
  117.         if (NULL == psiT) {
  118.             psiT = (SI *) malloc(sizeof(SI)); /** and create entry **/
  119.             if (0 > cmp)
  120.                 psiOld->psiL = psiT;
  121.             else
  122.                 psiOld->psiR = psiT;
  123.             psiT->fnSample = strdup(fnSample);
  124.             psiT->pitch = -1;
  125.             psiT->sample = -1;
  126.             psiT->psiL = psiT->psiR = NULL;
  127.         }
  128.     }
  129.     return psiT;
  130. }
  131.  
  132. /*
  133.  * PsiPrunePsi: Returns the given sample tree, but without any redundant
  134.  * samples. Any redundant samples are freed. If no samples remain, NULL
  135.  * is returned.
  136.  */
  137. SI *PsiPrunePsi(SI *psi)
  138. {
  139.     if (NULL == psi)
  140.         return NULL;
  141.     psi->psiL = PsiPrunePsi(psi->psiL); /** Prune left and right branches **/
  142.     psi->psiR = PsiPrunePsi(psi->psiR);
  143.     if (-1 == psi->pitch) { /** If root of tree redundant, need to remove it **/
  144.         SI *psiT;
  145.  
  146.         if (NULL == psi->psiL) { /** If no left branch **/
  147.             psiT = psi->psiR;
  148.             free(psi); /** replace root **/
  149.             psi = psiT; /** with right branch **/
  150.         } else if (NULL == psi->psiR) { /** If no right branch **/
  151.             psiT = psi->psiL;
  152.             free(psi); /** replace root **/
  153.             psi = psiT; /** with left branch **/
  154.         } else if (NULL == psi->psiL->psiR) { /** If left branch has no right **/
  155.             psiT = psi->psiL;
  156.             psiT->psiR = psi->psiR; /** put right branch on right of left **/
  157.             free(psi); /** and replace root **/
  158.             psi = psiT; /** with left branch **/
  159.         } else { /** Else.. there's 2 full branches - yuck! **/
  160.             SI *psiOld;
  161.  
  162.             psiT = psi->psiL;
  163.             while (NULL != psiT->psiR) { /** Find rightmost entry on left branch **/
  164.                 psiOld = psiT;
  165.                 psiT = psiT->psiR;
  166.             }
  167.             psiOld->psiR = psiT->psiL;
  168.             psiT->psiL = psi->psiL;
  169.             psiT->psiR = psi->psiR;
  170.             free(psi); /** remove root **/
  171.             psi = psiT; /** and replace it with that entry **/
  172.         }
  173.     }
  174.     return psi;
  175. }
  176.  
  177. /*
  178.  * ReadconfigFn: Given the filename of the configuration file, it interprets
  179.  * each line and sets up options and sample-tables.
  180.  */
  181. void ReadconfigFn(Sz fnConfig)
  182. {
  183.     FILE *pfileConfig;
  184.     Sz szLine, szTok;
  185.     int csz = 0, fError = 0;
  186.  
  187.     if (NULL == (pfileConfig = fopen(fnConfig, "rt"))) {
  188.         fprintf(stderr, "ptmid: Cannot find config file: %s\n", fnConfig);
  189.         exit(1);
  190.     }
  191.     while ((szLine = SzReadPfile(pfileConfig)) != NULL) { /** With every line.. **/
  192.         csz++;
  193.         if ('#' != szLine[0] && NULL != (szTok = strtok(szLine, " \t")))
  194.             if ('0' <= szTok[0] && '9' >= szTok[0] || !strcmp(szTok, "def")) {
  195.                 int irgppsi, cpsi; /** If an instrument definition **/
  196.                 SI **ppsi;
  197.  
  198.                 if ('d' == szTok[0])
  199.                     irgppsi = 128;
  200.                 else
  201.                     irgppsi = atoi(szTok); /** decode instrument **/
  202.                 if (irgppsi < 129)
  203.                     while (NULL != (szTok = strtok(NULL, " \t"))) { /*** With every sample.. ***/
  204.                         if (NULL == rgppsiIns[irgppsi]) /*** Ensure allocated ***/
  205.                             rgppsiIns[irgppsi] = ppsi = (SI **) malloc(sizeof(SI *) * 2);
  206.                         else {
  207.                             ppsi = rgppsiIns[irgppsi];
  208.                             for (cpsi = 2; NULL != *ppsi; cpsi++, ppsi++);
  209.                             rgppsiIns[irgppsi] = ppsi = (SI **) realloc(rgppsiIns[irgppsi],
  210.                                 sizeof(SI *) * cpsi);
  211.                             ppsi += cpsi - 2;
  212.                         }
  213.                         ppsi[0] = PsiAddsample(szTok); /*** Put sample in array ***/
  214.                         ppsi[1] = NULL;
  215.                     }
  216.                 else
  217.                     fError = 1;
  218.  
  219.             } else if ('d' == szTok[0] && '0' <= szTok[1] && '9' >= szTok[1]) {
  220.                 int irgpsi; /** If a percussion definition **/
  221.  
  222.                 if ((irgpsi = atoi(szTok + 1)) < 128 && /** decode instrument **/
  223.                  (szTok = strtok(NULL, " \t")) != NULL) {
  224.                     if (NULL != rgpsiDrum[irgpsi])
  225.                         free(rgpsiDrum[irgpsi]); /** and free up if previously used **/
  226.                     rgpsiDrum[irgpsi] = PsiAddsample(szTok); /** Put sample in array **/
  227.                 } else
  228.                     fError = 1;
  229.  
  230.             } else if (!strcmp(szTok, "sample")) { /** If sample info **/
  231.                 Sz fnSample;
  232.                 SI *psi;
  233.                 int irgb;
  234.                 static int rgbPitch[7] = {45, 47, 36, 38, 40, 41, 43};
  235.  
  236.                 if ((fnSample = strtok(NULL, " \t")) == NULL) /** Get name **/
  237.                     fError = 1;
  238.                 else if ((szTok = strtok(NULL, " \t")) == NULL)
  239.                     fError = 1;
  240.                 else if ((irgb = toupper(szTok[0]) - 'A') < 0 || 6 < irgb) /** Get pitch **/
  241.                     fError = 1;
  242.                 else {
  243.                     psi = PsiAddsample(fnSample); /** Make sure sample allocated **/
  244.                     if ('#' == szTok[1])
  245.                         szTok++;
  246.                     psi->pitch = rgbPitch[irgb] + 12 * atoi(szTok + 1) +
  247.                         ('#' == szTok[0] ? 1 : 0);
  248.                     if ((szTok = strtok(NULL, " \t")) == NULL)
  249.                         psi->wLppos = 0;
  250.                     else
  251.                         psi->wLppos = atoi(szTok); /** Get loop start **/
  252.                     if ((szTok = strtok(NULL, " \t")) == NULL)
  253.                         psi->wLplen = 0;
  254.                     else
  255.                         psi->wLplen = atoi(szTok); /** Get loop length **/
  256.                 }
  257.  
  258.             } else if (!strcmp(szTok, "formid")) /** If module-id **/
  259.                 if ((szTok = strtok(NULL, " \t")) == NULL)
  260.                     fError = 1;
  261.                 else
  262.                     strncpy(szId, szTok, 4); /** store it **/
  263.  
  264.             else if (!strcmp(szTok, "patmax")) /** If max. patterns **/
  265.                 if ((szTok = strtok(NULL, " \t")) == NULL)
  266.                     fError = 1;
  267.                 else {
  268.                     int wT;
  269.  
  270.                     if ((wT = atoi(szTok)) != 0 && 128 >= wT)
  271.                         wPatmax = wT; /** store them **/
  272.                     else
  273.                         fError = 1;
  274.                 }
  275.  
  276.             else if (!strcmp(szTok, "ptchan")) /** If max. channels **/
  277.                 if ((szTok = strtok(NULL, " \t")) == NULL)
  278.                     fError = 1;
  279.                 else {
  280.                     int wT;
  281.  
  282.                     if ((wT = atoi(szTok)) != 0 && MAXPTCHAN >= wT)
  283.                         wPtchan = wT; /** store them **/
  284.                     else
  285.                         fError = 1;
  286.                 }
  287.  
  288.             else if (!strcmp(szTok, "drumch")) /** If MIDI percussion channel **/
  289.                 if ((szTok = strtok(NULL, " \t")) == NULL)
  290.                     fError = 1;
  291.                 else {
  292.                     int bT;
  293.  
  294.                     if ((bT = atoi(szTok)) != 0)
  295.                         bDrumch = bT - 1; /** store it **/
  296.                 }
  297.  
  298.             else if (!strcmp(szTok, "fract") && NULL == szQuant) { /** If quantize frac. **/
  299.                 int wT;
  300.  
  301.                 if ((szQuant = strtok(NULL, " \t")) == NULL ||
  302.                  !(wT = ValidquantSz(strlwr(szQuant)))) /** decode **/
  303.                     fError = 1;
  304.                 else
  305.                     wQuantval = wT; /** and store it **/
  306.  
  307.             } else if (!strcmp(szTok, "extend")) /** If extend-notes flag **/
  308.                 fExtend = 1; /** toggle **/
  309.             else if (!strcmp(szTok, "nocopy")) /** If no-copyright flag **/
  310.                 fNocopy = 1; /** toggle **/
  311.             else
  312.                 fError = 1;
  313.  
  314.             if (fError) { /** If an error at any point, reveal line **/
  315.                 fprintf(stderr, "ptmid: Error in config file: line %d\n", csz);
  316.                 exit(1); /** and quit **/
  317.             }
  318.         free(szLine);
  319.     }
  320.  
  321.     if (NULL == rgppsiIns[128]) {
  322.         fprintf(stderr, "ptmid: No default instrument defined in config file\n");
  323.         exit(1);
  324.     }
  325.     if ((psiTree = PsiPrunePsi(psiTree)) == NULL) {
  326.         fprintf(stderr, "ptmid: No sample definitions found in config file\n");
  327.         exit(1);
  328.     }
  329. }
  330.  
  331. /*
  332.  * main: Parses arguments to program and opens appropriate MOD and MID files.
  333.  */
  334. int main(int argc, char **argv)
  335. {
  336.     int cNames = 0;
  337.     Sz fnDef, fnConfig = DEFCONFIG;
  338.     Fn fnIn, fnOut;
  339.     FILE *pfileMod;
  340.     Tune *ptuneMusic;
  341.  
  342.     Init();
  343.     for (argv++; NULL != *argv; argv++) /** Run through all parameters **/
  344.         if ('-' == **argv) /** If parameter is a switch **/
  345.             switch ((*argv)[1]) { /** check what sort **/
  346.                 case 'c':
  347.                     if ((*argv)[2])
  348.                         fnConfig = *argv + 2; /** c: set config file **/
  349.                     break;
  350.                 case 'd':
  351.                     bDrumch = atoi(*argv + 2) - 1; /** d: set drum channel **/
  352.                     break;
  353.                 case 'f': {
  354.                     int wT; /** f: set quantize fraction **/
  355.  
  356.                     if ((wT = ValidquantSz(strlwr(*argv + 2))))
  357.                         wQuantval = wT;
  358.                     else
  359.                         fprintf(stderr, "ptmid: Invalid quantize fraction - using default\n");
  360.                     break;
  361.                 }
  362.                 case 'q':
  363.                     fQuiet = !fQuiet; /** q: toggle quiet mode **/
  364.                     break;
  365.             }
  366.         else { /** Else must be a filename **/
  367.             if (0 == cNames)
  368.                 BuildFn(fnIn, fnDef = *argv, "mid");
  369.             else if (1 == cNames)
  370.                 BuildFn(fnOut, *argv, "mod");
  371.             cNames++;
  372.         }
  373.  
  374.     if (1 > cNames || 2 < cNames) { /** If no filenames - error **/
  375.         printf("Use: ptmid [-cFile] [-dChannel] [-fFrac] [-q] infile[.mid] [outfile[.mod]]\n");
  376.         printf("  version " PTVER "\n");
  377.         printf("  Creates Protracker MOD files from General MIDI files\n");
  378.         exit(1);
  379.     } else if (1 == cNames) /** If one filename **/
  380.         BuildFn(fnOut, fnDef, "mod"); /** build other one **/
  381.     if (!fQuiet)
  382.         printf("ptmid ver " PTVER ": Converting '%s' to '%s'\n", fnIn, fnOut);
  383.  
  384.     ReadconfigFn(fnConfig);
  385.     if (access(fnIn, 1)) {
  386.         fprintf(stderr, "ptmid: Cannot access file: %s\n", fnIn);
  387.         exit(1);
  388.     }
  389.     if ((ptuneMusic = PtuneLoadFn(fnIn)) == NULL) {
  390.         fprintf(stderr, "ptmid: Not a legal MIDI file: %s\n", fnIn);
  391.         exit(1);
  392.     }
  393.  
  394.     if (!fQuiet)
  395.         printf("Analyzing..\n");
  396.     ResolvePtune(ptuneMusic);
  397.  
  398.     if (!fQuiet)
  399.         printf("Writing..\n");
  400.     if ((pfileMod = fopen(fnOut, "wb")) == NULL) {
  401.         fprintf(stderr, "ptmid: Cannot create file: %s\n", fnOut);
  402.         exit(1);
  403.     }
  404.     SavePtunePfile(ptuneMusic, pfileMod);
  405.     fclose(pfileMod);
  406.  
  407.     if (!fQuiet)
  408.         printf("Done.\n");
  409. }
  410.